﻿using fasc_openapi_donet_sdk.Attributes;
using fasc_openapi_donet_sdk.Common;
using fasc_openapi_donet_sdk.Model.RequestModel;
using fasc_openapi_donet_sdk.Model.RequestModel.ClientOrgManage;
using fasc_openapi_donet_sdk.Model.RequestModel.ClientSignTask;
using fasc_openapi_donet_sdk.Model.ResponseModel;
using fasc_openapi_donet_sdk.Model.ResponseModel.ClientSignTask;
using Newtonsoft.Json;
using System;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Net;
using System.Text;
using System.Threading.Tasks;
using System.Web;

namespace fasc_openapi_donet_sdk.Client
{
    public class OpenApiClient
    {
        private string AppId;
        private string AppSecret;
        private string ServerUrl;

        private const string signType = "HMAC-SHA256";//签名算法

        //Token对象存储
        private static ConcurrentDictionary<string, AccessTokenRes> tokens = new ConcurrentDictionary<string, AccessTokenRes>();

        public OpenApiClient(string appId,string appSecret,string serverUrl)
        {
            this.AppId = appId;
            this.AppSecret = appSecret;
            this.ServerUrl = serverUrl;
        }

        /// <summary>
        /// 获取AccessToken
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="req"></param>
        /// <returns></returns>
        public BaseRes<T> GetHttpResponse<T>(BaseReq<T> req,string accessToken) where T : class,new()
        {
            var reqStr = JsonConvert.SerializeObject(req,Formatting.Indented,new JsonSerializerSettings() { NullValueHandling = NullValueHandling.Ignore });          
            var urlAttribute = GetUrlAttribute(req);
            var url = ServerUrl + urlAttribute.Url;
            BaseRes<T> rspModel = null;
            try
            {
                var body = new Dictionary<string, string>() { { "bizContent", reqStr } };
                var client = HttpHelper.CreateDefault(url);
              //  var tokenData = GetToken();
                if (accessToken != null)
                {
                    SetReqHeadParams(client, accessToken, body);
                    client.Method = urlAttribute.Method;
                    client.ContentType = "application/x-www-form-urlencoded";       
                    client.SubmitFormData(body, null);
                    var rsp = client.GetResponseIgnoreServerError();
                    string rspStr = null;
                    if (typeof(DownloadFilesRes).IsAssignableFrom(typeof(T)))
                    {
                        if (rsp.ContentType.StartsWith("application/json"))
                        {
                            rspStr = rsp.GetResponseString();
                            rspModel = JsonConvert.DeserializeObject<BaseRes<T>>(rspStr);
                            rspModel.requestId = rsp.Headers["X-FASC-Request-Id"];
                        }
                        else
                        {
                            System.IO.Stream stream = rsp.GetResponseStream();
                            DownloadFilesRes downloadFilesRes = new DownloadFilesRes()
                            {
                                fileStream = stream,
                                contentType = rsp.ContentType
                            };
                            rspModel = new BaseRes<T>()
                            {
                                code = "10000",
                                msg = "文件下载成功！",
                                requestId = rsp.Headers["X-FASC-Request-Id"],
                                data = downloadFilesRes as T
                            };
                        }
                    }
                    else
                    {
                        rspStr = rsp.GetResponseString();
                        rspModel = JsonConvert.DeserializeObject<BaseRes<T>>(rspStr);
                        rspModel.requestId = rsp.Headers["X-FASC-Request-Id"];
                    }
                }
                else
                {
                    rspModel = new BaseRes<T>()
                    {
                        code = "100002",
                        msg = "请检查accessToken是否存在，若不存在或已过期，请重新获取",
                        data = null
                    };
                }

            }
            catch (Exception ex)
            {
                rspModel = new BaseRes<T>()
                {
                    code = "-1",
                    msg = "clientResquest" + ex.Message,
                };
            }
            return rspModel;
        }


        /// <summary>
        /// 上传即时文件
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="req"></param>
        /// <param name="fileContent"></param>
        /// <returns></returns>
        public BaseRes<T> GetUploadFileResponse<T>(BaseReq<T> req,FileItem fileContent, string accessToken) where T : class, new()
        {
            var reqStr = JsonConvert.SerializeObject(req, Formatting.Indented, new JsonSerializerSettings() { NullValueHandling = NullValueHandling.Ignore });
            var urlAttribute = GetUrlAttribute(req);
            var url = ServerUrl + urlAttribute.Url;
            BaseRes<T> rspModel = null;
            try
            {
                var body = new Dictionary<string, string>() { { "bizContent", reqStr } };
                var files = GetRequestFiles(req,fileContent);
                var client = HttpHelper.CreateDefault(url);
               // var tokenData = GetToken();
                if (accessToken != null)
                {
                    SetReqHeadParams(client, accessToken, body);
                    client.Method = urlAttribute.Method;
                    client.ContentType = "multipart/form-data";
                    client.SubmitFormData(body, files);
                    var rsp = client.GetResponseIgnoreServerError();
                    string rspStr = null;
                    if (typeof(FileItem).IsAssignableFrom(typeof(T)))
                    {
                        if (rsp.ContentType.StartsWith("application/json"))
                        {
                            rspStr = rsp.GetResponseString();
                            rspModel = JsonConvert.DeserializeObject<BaseRes<T>>(rspStr);
                            rspModel.requestId = rsp.Headers["X-FASC-Request-Id"];
                        }
                        else
                        {
                            var file = new T() as FileItem;
                            file.Stream = new MemoryStream();  //此处可根据request配置，直接传递filestream，直接就下载到本地存储，可以不用先下载到内存。
                            rsp.GetResponseFileItem(file);
                            rspModel = new BaseRes<T>()
                            {
                                code = "10000",
                                msg = "文件下载成功！",
                                data = file as T
                            };
                        }
                    }
                    else
                    {
                        rspStr = rsp.GetResponseString();
                        rspModel = JsonConvert.DeserializeObject<BaseRes<T>>(rspStr);
                        rspModel.requestId = rsp.Headers["X-FASC-Request-Id"];
                    }
                }
                else
                {
                    rspModel = new BaseRes<T>()
                    {
                        code = "100002",
                        msg = "请检查accessToken是否存在，若不存在或已过期，请重新获取",
                        data = null
                    };
                }

            }
            catch (Exception ex)
            {
                rspModel = new BaseRes<T>()
                {
                    code = "-1",
                    msg = "clientResquest" + ex.Message,
                };
            }
            return rspModel;
        }

        /// <summary>
        /// 获取服务访问凭证token
        /// </summary>
        /// <returns></returns>
        public BaseRes<AccessTokenRes> GetTokenFromServer()
        {
            try
            {
                AccessTokenRes res = new AccessTokenRes();
                var headParams = GetReqHeadParams();

                var client = HttpHelper.CreateDefault(ServerUrl + "/service/get-access-token");

                foreach (var item in headParams)
                {
                    client.Headers[item.Key] = item.Value;
                }
                client.Method = "POST";
                client.ContentType = "application/x-www-form-urlencoded";
                var rspResponse = client.GetResponseIgnoreServerError();

                var rspStr = rspResponse.GetResponseString();
                var rspToken = JsonConvert.DeserializeObject<BaseRes<AccessTokenRes>>(rspStr);
                rspToken.requestId = rspResponse.Headers["X-FASC-Request-Id"];

                if (rspToken.data != null)
                {
                    rspToken.data.expiresTime = DateTime.Now.AddSeconds(Convert.ToDouble(rspToken.data.expiresIn));
                }             
                return rspToken;
            }
            catch(Exception ex)
            {
                NLogHelper.Error("获取token异常" + ex.Message);
                return null;
            }
        }

        //获取类名上方配置的url地址
        private RemoteServiceAttribute GetUrlAttribute<T>(BaseReq<T> req) where T : class, new()
        {
            var urlAttribute = req.GetType().GetCustomAttributes(false).First(r => r is RemoteServiceAttribute) as RemoteServiceAttribute;
            return urlAttribute;
        }

        /// <summary>
        /// 拼接header请求参数
        /// </summary>
        /// <returns></returns>
        public IDictionary<string,string> GetReqHeadParams()
        {
            IDictionary<string, string> headerParams = new  Dictionary<string,string>();
            string nonce = Guid.NewGuid().ToString("N");
            //标准时间戳
            string strTimeStamp = ((DateTime.Now.ToUniversalTime().Ticks - 621355968000000000) / 10000).ToString();//毫秒值
            headerParams.Add("X-FASC-App-Id", AppId);
            headerParams.Add("X-FASC-Sign-Type", signType);
            headerParams.Add("X-FASC-Timestamp", strTimeStamp);
            headerParams.Add("X-FASC-Nonce", nonce);
            headerParams.Add("X-FASC-Grant-Type", "client_credential");

            string signature = Common.SignUtil.GetSignByParams(headerParams, strTimeStamp, AppSecret);

            headerParams.Add("X-FASC-Sign", signature);

            return headerParams;
        }

        /// <summary>
        /// 拼接公共请求head参数
        /// </summary>
        /// <param name="client"></param>
        /// <param name="accessToken"></param>
        /// <param name="body"></param>
        public void SetReqHeadParams(HttpWebRequest client,string accessToken,Dictionary<string,string> body)
        {
            IDictionary<string, string> headerParams = new Dictionary<string, string>();
            string nonce = Guid.NewGuid().ToString("N");
            //标准时间戳
            string strTimeStamp = ((DateTime.Now.ToUniversalTime().Ticks - 621355968000000000) / 10000).ToString();//毫秒值
            headerParams.Add("X-FASC-App-Id", AppId);
            headerParams.Add("X-FASC-Sign-Type", signType);
            headerParams.Add("X-FASC-Timestamp", strTimeStamp);
            headerParams.Add("X-FASC-Nonce", nonce);
            headerParams.Add("X-FASC-AccessToken", accessToken);
            string signature = Common.SignUtil.GetSignByParams(headerParams.Union(body), strTimeStamp, AppSecret);

            headerParams.Add("X-FASC-Sign", signature);
            foreach (var p in headerParams)
            {
                client.Headers[p.Key] = p.Value;
            }
        }

        //获取token并判断失效时间
        public BaseRes<AccessTokenRes> GetToken()
        {
            BaseRes<AccessTokenRes> tokenData = new BaseRes<AccessTokenRes>();
            if (tokens.TryGetValue(AppId, out tokenData.data) == false || tokenData.data == null)
            {
                tokenData = GetTokenFromServer();
                tokens[AppId] = tokenData.data;
            }
            else if ((tokenData.data.expiresTime - DateTime.Now).TotalMinutes < 3)
            {
                tokenData = GetTokenFromServer();
                tokens[AppId] = tokenData.data;
            }
            return tokenData;
        }

        private IDictionary<string, FileItem> GetRequestFiles<T>(BaseReq<T> req,FileItem fileContent) where T : class, new()
        {
            IDictionary<string, FileItem> files = new Dictionary<string, FileItem>();
            var filePropertys = req.GetType().GetProperties().Where(r => r.PropertyType == typeof(FileItem));
            if (filePropertys.Count() > 0)
            {
                foreach (var property in filePropertys)
                {
                    var file = property.GetValue(req, null) as FileItem;
                    files.Add(property.Name, file);
                }
            }
            else if(fileContent != null)
            {
                files.Add("fileContent", fileContent);
            }
            return files;
        }

        /// <summary>
        /// 异步回调事件
        /// </summary>
        /// <returns></returns>
        public string FddEventCallBack(HttpWebRequest request,string bizContent)
        {
            var body = new Dictionary<string, string>() { { "bizContent", bizContent } };
            string appSecret = "";
            //获取请求头参数
            string appId = request.Headers["X-FASC-App-Id"];
            string signType = request.Headers["X-FASC-Sign-Type"];
            string sign = request.Headers["X-FASC-Sign"];
            string timestamp = request.Headers["X-FASC-Timestamp"];
            string eventId = request.Headers["X-FASC-Event"];
            string nonce = request.Headers["X-FASC-Nonce"];

            //验签
            IDictionary<string, string> headerParams = new Dictionary<string, string>();
            //标准时间戳
            string strTimeStamp = ((DateTime.Now.ToUniversalTime().Ticks - 621355968000000000) / 10000).ToString();//毫秒值
            headerParams.Add("X-FASC-App-Id", appId);
            headerParams.Add("X-FASC-Sign-Type", signType);
            headerParams.Add("X-FASC-Timestamp", timestamp);
            headerParams.Add("X-FASC-Nonce", nonce);
            headerParams.Add("X-FASC-Event", eventId);
            string signature = Common.SignUtil.GetSignByParams(headerParams.Union(body), strTimeStamp, AppSecret);

            if (signature.Equals(sign))
            {
                //返回success中断回调机制
                return "{\"msg\":\"success\"}";
            }
            return "{\"msg\":\"success\"}";

        }

        /// <summary>
        /// 构造企业用户授权链接
        /// </summary>
        /// <returns></returns>
        public string ComposeCorpAuthUrl(ComposeCorpAuthUrlReq req)
        {
            //标准时间戳
            string strTimeStamp = ((DateTime.Now.ToUniversalTime().Ticks - 621355968000000000) / 10000).ToString();//毫秒值
            string encodeUrl = HttpUtility.UrlEncode(req.redirectUrl, Encoding.UTF8);
            req.redirectUrl = encodeUrl;
            string signature = GetCorpAuthSign(req, strTimeStamp);
            string resultUrl = GetDomainUrl(ServerUrl, AppId) + "/authorize/list?authScopes=" + req.authScopes + "&corpName=" + req.corpName + "&openCorpId=" + req.openCorpId + "&redirectUrl=" + encodeUrl + "&signature=" + signature + "&timestamp=" + strTimeStamp;

            return resultUrl;
        }

        /// <summary>
        /// 计算构造企业用户授权链接所需的签名值
        /// </summary>
        /// <returns></returns>
        public string GetCorpAuthSign(ComposeCorpAuthUrlReq req,string strTimeStamp)
        {
            IDictionary<string, string> signParams = new Dictionary<string, string>();

            signParams.Add("appId", AppId);
            signParams.Add("openCorpId", req.openCorpId);
            signParams.Add("corpName", req.corpName);
            signParams.Add("redirectUrl", req.redirectUrl);
            signParams.Add("authScopes", req.authScopes);
            string signature = Common.SignUtil.GetSignByParams(signParams, strTimeStamp, AppSecret);

            return signature;
        }

        /// <summary>
        /// 获取domain地址
        /// </summary>
        /// <param name="requestURL"></param>
        /// <param name="appId"></param>
        /// <returns></returns>
        public string GetDomainUrl(string requestUrl,string appId)
        {
            Dictionary<string, string> domains = new Dictionary<string, string>();
            domains.Add("https://sit-api.fadada.com/api/v5/", "https://" + AppId + ".sit-e.fadada.com");
            domains.Add("https://uat-api.fadada.com/api/v5/", "https://" + AppId + ".uat-e.fadada.com");
            domains.Add("https://api.fadada.com/api/v5/", "https://" + AppId + ".e.fadada.com");
            return domains[requestUrl];
        }
    }
}
